/**
 * Copyright (c) 2010 Claudio Imbrenda.
 * This program is released under the terms of the GNU GPL License, version 3 or later.
 */
 
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
                     

#include "visit.h"
#include "methods.h"

#define METHODS 5
static char* methodnames[] ={ "Exahustive", "Random Sampling", "Median Node Visit", 
      "Median Node Tree", "Fringe", 
      NULL};
      
static char* loadmodes[] ={"Text file", "Binary file", "SHM segment", NULL};

static void printhelp(){
 int cx;
 fprintf(ERR,"usage: \ttntesi [-b] [-mXX] [-iXX] inputfile\n");
 fprintf(ERR,"\t-b\tBinary input format (default: text format)\n");
 fprintf(ERR,"\t-s\tSHM input format (default: text format)\n");
 fprintf(ERR,"\t-mXX\tUse method XX (default: 0)\n");
 fprintf(ERR,"\t-iXX\tNumber of iterations to perform (default: 10)\n");
 fprintf(ERR,"\t-kXX\tMax number of visits per iterations to perform (default: 1000)\n");
 fprintf(ERR,"\t-vXX\tMax number of visits (default: i*k)\n");
 fprintf(ERR,"\t-tXX,XX\tTarget minimum and maximum (default: none)\n");
 fprintf(ERR,"\t-uXX\tNumber of runs ti perform (default: 1)\n");
 fprintf(ERR,"\t-rXX\tSet pseodorandom generator seed. (default: random)\n");
 fprintf(ERR,"Available methods: \n");
 for(cx=0;cx<METHODS;cx++){
   fprintf(ERR,"  %d.\t%s\n",cx,methodnames[cx]);
 }
}

static void fail(char*message){
 fprintf(ERR,"ERROR! %s\n",message);
 exit(1);
}

/** 
 Generates a good seed for the pseudorandom number generator.
 @return a good seed for the pseudorandom number generator.
*/
static uint mkrandomseed(){
 struct timeval tt;
 uint s;
 uint uninitialized; /* this is supposed to be left uninitialized */
 
 if(gettimeofday(&tt,NULL)==0){
  s= (uint)(tt.tv_sec + tt.tv_usec);
 }else
  s=time(NULL);
 
 s+=uninitialized;
#ifdef HAS_DEVRANDOM
 {int r=0;
  int fd;
  fd=open("/dev/random",O_RDONLY);
  if(fd!=-1){
   fprintf(ERR,"Using /dev/random!\n");
   if(read(fd,&r,sizeof(uint))!=sizeof(uint))
     fprintf(ERR,"WARNING: short read from /dev/urandom!\n");
   s+=r;
   close(fd);
  } else fprintf(ERR,"Could NOT use /dev/random!\n");  
 }
#endif
 return s;
}

int main(int argc, char*argv[]){
 struct tngraph* g;
 int a,min=0,max=65536;
 int k=1000;
 long long b;
 int method=0, iterations=10;
 int v=iterations*k;
 int uberit=1;
 char* filename = "input.txt";
 int mode=GRAPH_MODE_TEXT;
 uint randomseed;
 int seedset=0;

 /* disable the write buffers on stderr and stdout */ 
 setvbuf(stdout,NULL,_IONBF,0);
 setvbuf(stderr,NULL,_IONBF,0);
 
 if(argc<2) fail("Not enough parameters. Try -h for help.");
 
 /* Parsing the commandline arguments */
 for(a=1;a<argc;a++){
  if(argv[a][0]=='-'){
   if(argv[a][1]=='m'){
     if((argv[a][2]>='0')&&(argv[a][2]<='9')){
       method=atoi(argv[a]+2);
       if(method==10)iterations=-1;
       if(method>=METHODS)
        fail("Wrong or unimplemented method. (Try -h for help)");
     }else fail("Wrong parameters. (Try -h for help)");
   }else if(argv[a][1]=='i'){
     iterations=atoi(argv[a]+2);
     v=iterations*k;
   }else if(argv[a][1]=='k'){
     k=atoi(argv[a]+2);
     if(k<5){
      fprintf(ERR,"WARNING! setting k=5.\n\tk=%d cannot accomodate the overhead of most methods!\n",k);
      k=5;
     }
     v=iterations*k;
   }else if(argv[a][1]=='v'){
     v=atoi(argv[a]+2);
   }else if(argv[a][1]=='u'){
     uberit=atoi(argv[a]+2);
   }else if(argv[a][1]=='t'){
     if(sscanf(argv[a]+2,"%d,%d",&targetmin,&targetmax)!=2)
      targetmin=targetmax=-1;
   }else if(argv[a][1]=='r'){
     if(sscanf(argv[a]+2,"%u",&randomseed)==1) seedset=1;
   }else if(argv[a][1]=='b'){
     mode=GRAPH_MODE_BINARY;
   }else if(argv[a][1]=='s'){
     mode=GRAPH_MODE_MMAP;
   }else if(argv[a][1]=='h'){
     printhelp();
     exit(0);
   }else fail("Wrong parameters. (Try -h for help)");
  }
  else filename=argv[a];
 }
 
 /* sets a random seed for the pseudorandom number generator, if one was not set explicitly*/
 if(!seedset)
  randomseed=mkrandomseed(); 
 printf("#Seed used: %u\n",randomseed);
 srandom(randomseed);
 
 /* initialization */
 maxvisits=v; 
 
 /* prints the output header and loads the graph */ 
 fprintf(ERR,"Method %d (%s); max %d iterations, max %d visits per iteration, max %d visits in total.\n",method,methodnames[method],iterations,k,v);
 fprintf(ERR,"loading graph from %s (%s format)..",filename,loadmodes[mode]);
 g=loadgraph(filename, mode);
 if(g==NULL){
  fprintf(ERR,"File \"%s\" not found, or wrong format. ",filename);
  fail("Cannot open input file.");
 }
 fprintf(ERR,"done.\n");
 
 /* counts nodes and edges before removing the unconnected components */
 a=nodecount(g);
 b=edgecount(g);

 fprintf(ERR,"Graph loaded, %d nodes, %lld edges.\n",a,b);

 /* finds the gigant component and removes the unconnected nodes */
 fprintf(ERR,"Finding gigant component..");
 findgigantandkill(g);
 fprintf(ERR,"done.\n");
 
 /* counts nodes and edges after the removal of the unconnected components */
 a=nodecount(g);
 b=edgecount(g);
 fprintf(ERR,"Graph sanitized, %d nodes, %lld edges.\n",a,b);
 fprintf(ERR,"Performing %d runs.\n",uberit);

 /* perform the given number of iterations of the given method */ 
 while(uberit--){
  visits=0;min=0;max=65536;
  fprintf(ERR,"Running method %d (%s) for %d iterations...\n",method,methodnames[method],iterations);
  printf("#starting timestamp: %lld\n",(long long)time(NULL));
  switch(method){
   case 0: a=exhaustivemethod(g,iterations,k,&min,&max);
           break; 
   case 1: a=samplingmethod(g,iterations,k,&min,&max);
           break;
   case 2: a=medianvisitmethod(g,iterations,k,&min,&max);
           break;
   case 3: a=mediantreemethod(g,iterations,k,&min,&max);
           break; 
   case 4: a=fringemethod(g,iterations,k,&min,&max);
           break;
   default: fail("Invalid method? try -h for help.");
  }
  a++;
  fprintf(ERR,"Result of method %d (%s) after %d iterations and %d visits:\n",method,methodnames[method],iterations,visits);
  fprintf(ERR,"%d,%d\n",min,max);
  fprintf(ERR,"#Best result after %d iterations (%d visits).\n",a,bestvisit);
  fflush(stdout);
 }
  
 freegraph(g);
 return 0;
}

